#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <stdint.h>
#include <error.h>
#include <errno.h>
#include <string.h>



#define INTERFACE_DESCRIPTION_BLOCK     0x00000001
#define PACKET_BLOCK                    0x00000002
#define SIMPLE_PACKET_BLOCK             0x00000003
#define NAME_RESOLUTION_BLOCK           0x00000004
#define INTERFACE_STATISTICS_BLOCK      0x00000005
#define ENHANCED_PACKET_BLOCK           0x00000006
#define SECTION_HEADER_BLOCK            0x0A0D0D0A

#define MIN_BLOCK_LEN   12
#define MAXLINE         256
#define ADJUST_ALIGN(len, bytes)    \
    ((len) + (((len) % (bytes)) ? ((bytes) - ((len) % (bytes))) : 0))
#define USAGE   "FATAL ERROR: Wrong number of arguments.\n" \
                "Usage: [-d] %s <input_filename>\n" \
                "   d   debug\n"


char *timestamp_str(char * bodyp, uint8_t if_tsresol);

int main(int argc, char *argv[])
{
    char *input_filename;
    FILE *input_filep;
    struct block_header_str {
        uint32_t type;
        uint32_t length;
    } block_header;
    uint32_t body_len;
    char block_body[BUFSIZ];
    char *block_bodyp;
    size_t bytes_read;
    uint16_t OpCod, OpLen;
    char *OpCodNam, OpValStr[MAXLINE];
    uint16_t RecTyp, RecLen;
    char *RecTypNam, RecValStr[MAXLINE];
    uint32_t i;
    int j, debug, opt;
    uint32_t CapLen;
    uint8_t if_tsresol;


    if ((argc < 2) || (argc > 3))
        error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__, USAGE, argv[0]);

    debug = 0;
    while ((opt = getopt(argc, argv, "d")) != -1) {
        switch (opt) {
            case 'd':
                debug = 1;
                break;
            default:
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        USAGE, argv[0]);
                exit(EXIT_FAILURE); 
        }
    }

    if (optind < argc)
        input_filename = argv[optind];
    else
        error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__, USAGE, argv[0]);

    if ((input_filep = fopen(input_filename, "r")) == NULL)
        error_at_line(EXIT_FAILURE, errno, __FILE__, __LINE__,
                "FATAL ERROR: %s", input_filename);

    while (!feof(input_filep)) {

        if ((bytes_read = (fread(&block_header, 1, sizeof(block_header),
                    input_filep))) != sizeof(block_header)) {
            if ((bytes_read == 0) && (feof(input_filep)))
                continue;
            else if (ferror(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "FATAL ERROR: Error on reading: %s.",
                        input_filename);
            else
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "INVALID FILE FORMAT: Block header not found.");
        }

        switch (block_header.type) {
            case INTERFACE_DESCRIPTION_BLOCK:
                printf("INTERFACE DESCRIPTION BLOCK\n");
                break;
            case PACKET_BLOCK:
                printf("PACKET BLOCK\n");
                break;
            case SIMPLE_PACKET_BLOCK:
                printf("SIMPLE PACKET BLOCK\n");
                break;
            case NAME_RESOLUTION_BLOCK:
                printf("NAME RESOLUTION BLOCK\n");
                break;
            case INTERFACE_STATISTICS_BLOCK:
                printf("INTERFACE STATISTICS BLOCK\n");
                break;
            case ENHANCED_PACKET_BLOCK:
                printf("ENHANCED PACKET BLOCK\n");
                break;
            case SECTION_HEADER_BLOCK:
                printf("SECTION HEADER BLOCK\n");
                break;
            default:
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "INVALID FILE FORMAT: Unknown block header type.");
                break;
        }

        body_len = ADJUST_ALIGN(block_header.length, sizeof(uint32_t))
            - sizeof(block_header);

        if (body_len > sizeof(block_body)) {
            if ((block_bodyp = malloc((size_t) body_len)) == NULL)
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "FATAL ERROR: Not enough memory.");
        } else {
            block_bodyp = block_body;
        }

        if (fread(block_bodyp, 1, body_len, input_filep) !=
                body_len) {
            if (feof(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "INVALID FILE FORMAT: Unexpected end of file.");
            else if (ferror(input_filep))
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "FATAL ERROR: Error on reading: %s.", 
                        input_filename);
            else
                error_at_line(EXIT_FAILURE, 0, __FILE__, __LINE__,
                        "FATAL ERROR: Unknown read error: %s.",
                        input_filename);
        }

        switch (block_header.type) {
            case INTERFACE_DESCRIPTION_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Link Type: %u\n",
                        *((uint16_t *) block_bodyp));
                printf("   Reserved: %u\n",
                        *((uint16_t *) block_bodyp + 2));
                printf("   SnapLen: %u\n",
                        *((uint32_t *) block_bodyp + 4));

                i = 8;
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "if_name";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 3:
                                OpCodNam = "if_description";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 4:
                                OpCodNam = "if_IPv4addr";
                                snprintf(OpValStr, MAXLINE,
                                        "%u.%u.%u.%u %u.%u.%u.%u",
                                        *((uint8_t *) (block_bodyp+i+4)),
                                        *((uint8_t *) (block_bodyp+i+5)),
                                        *((uint8_t *) (block_bodyp+i+6)),
                                        *((uint8_t *) (block_bodyp+i+7)),
                                        *((uint8_t *) (block_bodyp+i+8)),
                                        *((uint8_t *) (block_bodyp+i+9)),
                                        *((uint8_t *) (block_bodyp+i+10)),
                                        *((uint8_t *) (block_bodyp+i+11)));
                                break;

                            case 5:
                                OpCodNam = "if_IPv6addr";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 6:
                                OpCodNam = "if_MACaddr";
                                snprintf(OpValStr, MAXLINE,
                                        "%012lX",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            case 7:
                                OpCodNam = "if_EUIaddr";
                                snprintf(OpValStr, MAXLINE,
                                        "%016lX",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            case 8:
                                OpCodNam = "if_speed";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu bps",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            case 9:
                                OpCodNam = "if_tsresol";
                                if_tsresol = *((uint8_t *) 
                                        (block_bodyp+i+4));
                                snprintf(OpValStr, MAXLINE, 
                                        "%u", if_tsresol);
                                break;

                            case 10:
                                OpCodNam = "if_tzone";
                                snprintf(OpValStr, MAXLINE,
                                        "%08X",
                                        *((uint32_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            case 11:
                                OpCodNam = "if_filter";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 12:
                                OpCodNam = "if_os";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 13:
                                OpCodNam = "if_fcslen";
                                snprintf(OpValStr, MAXLINE,
                                        "%02X",
                                        *((uint8_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            case 14:
                                OpCodNam = "if_tsoffset";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)));
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }

                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }
                break;

            case PACKET_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Interface ID: %u\n",
                        *((uint16_t *) block_bodyp));
                printf("   Drops Count: %u\n",
                        *((uint16_t *) block_bodyp + 2));
                printf("   Timestamp: %s\n",
                       timestamp_str(block_bodyp + 4, if_tsresol));
                CapLen = *((uint32_t *) (block_bodyp + 12));
                printf("   Capture Len: %u\n", CapLen);
                printf("   Packet Len: %u\n",
                        *((uint32_t *) (block_bodyp + 16)));

                j = 0;
                for (i = 20; i < 20 + CapLen; i += 4) {
                    if (j == 0)
                        printf("%04X: ", i - 20);
                    printf(" %02X %02X %02X %02X",
                            *((uint8_t *) (block_bodyp + i)),
                            *((uint8_t *) (block_bodyp + i + 1)),
                            *((uint8_t *) (block_bodyp + i + 2)),
                            *((uint8_t *) (block_bodyp + i + 3)));
                    if (j == 3) {
                        j = 0;
                        putchar('\n');
                    } else
                        j += 1;
                }
                if (j != 0)
                    putchar('\n');
        
                i = 20 + ADJUST_ALIGN(CapLen, sizeof(uint32_t));
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "pack_flags";
                                snprintf(OpValStr, MAXLINE,
                                        "%08X",
                                        *((uint32_t *) (block_bodyp + 4)));
                                break;

                            case 3:
                                OpCodNam = "pack_hash";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }

                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }

                break;

            case SIMPLE_PACKET_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Packet Len: %u\n", 
                        *((uint32_t *) (block_bodyp)));
                CapLen = ADJUST_ALIGN(block_header.length, sizeof(uint32_t)) - 16;

                j = 0;
                for (i = 4; i < 4 + CapLen; i += 4) {
                    if (j == 0)
                        printf("%04X: ", i - 4);
                    printf(" %02X %02X %02X %02X",
                            *((uint8_t *) (block_bodyp + i)),
                            *((uint8_t *) (block_bodyp + i + 1)),
                            *((uint8_t *) (block_bodyp + i + 2)),
                            *((uint8_t *) (block_bodyp + i + 3)));
                    if (j == 3) {
                        j = 0;
                        putchar('\n');
                    } else
                        j += 1;
                }
                if (j != 0)
                    putchar('\n');
        
                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }
                break;

            case NAME_RESOLUTION_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                i = 0;
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        RecTyp = *((uint16_t *) (block_bodyp + i));
                        RecLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (RecTyp) {
                            case 0:
                                if (!debug) 
                                    RecTypNam = "";
                                else
                                    RecTypNam = "nres_endofrecord";
                                RecValStr[0] = '\0';
                                break;

                            case 1:
                                RecTypNam = "nres_ip4record";
                                snprintf(RecValStr, MAXLINE,
                                        "%d.%d.%d.%d %.*s", 
                                        *((uint8_t *)(block_bodyp + i + 4)),
                                        *((uint8_t *)(block_bodyp + i + 5)),
                                        *((uint8_t *)(block_bodyp + i + 6)),
                                        *((uint8_t *)(block_bodyp + i + 7)),
                                        RecLen - 4,
                                        block_bodyp + i + 8);
                                break;

                            case 2:
                                RecTypNam = "nres_ip6record";
                                snprintf(RecValStr, MAXLINE,
                                        "%8lX%8lX %.*s",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)),
                                        *((uint64_t *)
                                            (block_bodyp + i + 12)),
                                        RecLen - 16,
                                        block_bodyp + i + 20);
                                break;

                            default:
                                RecTypNam = "Unknown Record Type";
                                break;
                        }

                        if (!debug && RecTyp != 0)
                            printf("   %s: %s\n", RecTypNam, RecValStr);
                        else if (debug) {
                            printf("   Record Type: %s (%u)\n",
                                    RecTypNam, RecTyp);
                            printf("   Record Length: %u\n", RecLen);
                        }

                        i += ADJUST_ALIGN(RecLen, sizeof(uint32_t)) + 4;

                    } while ((RecTyp != 0) && (RecLen != 0));

                }

                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "ns_dnsname";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 3:
                                OpCodNam = "ns_dnsIP4addr";
                                snprintf(OpValStr, MAXLINE,
                                        "%d.%d.%d.%d", 
                                        *((uint8_t *)(block_bodyp + i + 4)),
                                        *((uint8_t *)(block_bodyp + i + 5)),
                                        *((uint8_t *)(block_bodyp + i + 6)),
                                        *((uint8_t *)(block_bodyp + i + 7)));
                                break;

                            case 4:
                                OpCodNam = "ns_dnsIP6addr";
                                snprintf(OpValStr, MAXLINE,
                                        "%8lX%8lX",
                                        *((uint64_t *)
                                            (block_bodyp + i + 4)),
                                        *((uint64_t *)
                                            (block_bodyp + i + 12)));
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }


                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }
                break;

            case INTERFACE_STATISTICS_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Interface ID: %u\n",
                        *((uint32_t *) block_bodyp));
                printf("   Timestamp: %s\n",
                        timestamp_str(block_bodyp + 4, if_tsresol));

                i = 12;
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "isb_starttime";
                                snprintf(OpValStr, MAXLINE,
                                        "%s", 
                                        timestamp_str(block_bodyp + i + 4, 
                                            if_tsresol));
                                break;

                            case 3:
                                OpCodNam = "isb_endtime";
                                snprintf(OpValStr, MAXLINE,
                                        "%s",
                                        timestamp_str(block_bodyp + i + 4, 
                                            if_tsresol));
                                break;

                            case 4:
                                OpCodNam = "isb_ifrecv";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            case 5:
                                OpCodNam = "isb_ifdrop";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            case 6:
                                OpCodNam = "isb_filteraccept";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            case 7:
                                OpCodNam = "isb_osdrop";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            case 8:
                                OpCodNam = "isb_usrdeliv";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }

                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }
                break;

            case ENHANCED_PACKET_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Interface ID: %u\n",
                        *((uint32_t *) block_bodyp));
                printf("   Timestamp: %s\n",
                       timestamp_str(block_bodyp + 4, if_tsresol));
                CapLen = *((uint32_t *) (block_bodyp + 12));
                printf("   Capture Len: %u\n", CapLen);
                printf("   Packet Len: %u\n",
                        *((uint32_t *) (block_bodyp + 16)));

                j = 0;
                for (i = 20; i < 20 + CapLen; i += 4) {
                    if (j == 0)
                        printf("%04X: ", i - 20);
                    printf(" %02X %02X %02X %02X",
                            *((uint8_t *) (block_bodyp + i)),
                            *((uint8_t *) (block_bodyp + i + 1)),
                            *((uint8_t *) (block_bodyp + i + 2)),
                            *((uint8_t *) (block_bodyp + i + 3)));
                    if (j == 3) {
                        j = 0;
                        putchar('\n');
                    } else
                        j += 1;
                }
                if (j != 0)
                    putchar('\n');
        
                i = 20 + ADJUST_ALIGN(CapLen, sizeof(uint32_t));
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "epb_flags";
                                snprintf(OpValStr, MAXLINE,
                                        "%08X",
                                        *((uint32_t *) (block_bodyp + 4)));
                                break;

                            case 3:
                                OpCodNam = "epb_hash";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 4:
                                OpCodNam = "epb_dropcount";
                                snprintf(OpValStr, MAXLINE,
                                        "%lu",
                                        *((uint64_t *) (block_bodyp + 4)));
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }

                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }

                break;

            case SECTION_HEADER_BLOCK:
                printf("   Block Type: %08X\n", block_header.type);
                printf("   Block Total Length: %u\n", 
                        block_header.length);
                printf("   Byte Order Magic: %08X\n",
                        *((uint32_t *) block_bodyp));
                printf("   Major Version: %u\n",
                        *((uint16_t *) (block_bodyp + 4)));
                printf("   Minor Version: %u\n",
                        *((uint16_t *) (block_bodyp + 6)));
                printf("   Section Length: %lu (0x%lX)\n",
                        *((uint64_t *) (block_bodyp + 8)),
                        *((uint64_t *) (block_bodyp + 8)));

                i = 16;
                if (body_len > (i + MIN_BLOCK_LEN)) {

                    do {
                        OpCod = *((uint16_t *) (block_bodyp + i));
                        OpLen = *((uint16_t *) (block_bodyp + i + 2));

                        switch (OpCod) {
                            case 0:
                                if (!debug) 
                                    OpCodNam = "";
                                else
                                    OpCodNam = "opt_endofopt";
                                OpValStr[0] = '\0';
                                break;

                            case 1:
                                OpCodNam = "opt_comment";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 2:
                                OpCodNam = "shb_hardware";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 3:
                                OpCodNam = "shb_os";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            case 4:
                                OpCodNam = "shb_userappl";
                                snprintf(OpValStr, MAXLINE,
                                        "%.*s", OpLen, block_bodyp + i + 4);
                                break;

                            default:
                                OpCodNam = "Unknown Optional Code";
                                break;
                        }

                        if (!debug && OpCod != 0)
                            printf("   %s: %s\n", OpCodNam, OpValStr);
                        else if (debug) {
                            printf("   Option Code: %s (%u)\n",
                                    OpCodNam, OpCod);
                            printf("   Option Length: %u\n", OpLen);
                        }

                        i += ADJUST_ALIGN(OpLen, sizeof(uint32_t)) + 4;

                    } while ((OpCod != 0) && (OpLen != 0));

                }

                if (!debug)
                    printf("\n");
                else {
                    printf("   Block Total Length: %u\n\n", 
                            *((uint32_t *) (block_bodyp + i)));
                }

                break;

            default:
                fprintf(stderr, "WARNING: Unknown Header type found.\n");

                break;
        }

    }

    exit(EXIT_SUCCESS);
}

char *timestamp_str(char * bodyp, uint8_t if_tsresol)
{
    int i;
    unsigned long int prec;
    time_t seconds;
    uint64_t dec_seconds;
    static char str[64];
    union {
        uint32_t word32[2];
        uint64_t timestamp;
    } wuint;

    wuint.word32[1] = *((uint32_t *) bodyp);
    wuint.word32[0] = *((uint32_t *) (bodyp + 4));

    if (if_tsresol & 0x80) {
        seconds = wuint.timestamp / (1 << (if_tsresol & 0x7F));
        dec_seconds = wuint.timestamp / (1 << (if_tsresol & 0x7F)) 
            - seconds;
    } else {
        prec = 1;
        for (i = 0; i < if_tsresol; i++)
            prec *= 10;
        seconds = wuint.timestamp / prec;
        dec_seconds = wuint.timestamp - seconds * prec;
    }

    strftime(str, sizeof(str), "%b %d, %Y %T", localtime(&seconds));
    snprintf(&str[strlen(str)], sizeof(str) - strlen(str), 
            ".%lu ", dec_seconds);
    strftime(&str[strlen(str)], sizeof(str) - strlen(str), "%Z", 
            localtime(&seconds));

    return str;
}
